use std::{collections::{hash_map::Entry, HashMap, HashSet}, thread::spawn};

use serde::{Deserialize, Serialize};
use wtask_base::{error::{WError, WResult}, export::{Local, kill_channel, select}, logs::log_format, machine::{random_token, runtime_init}, timer::TimerAppTrait};

use crate::{basic::{MachineConfigs, WEB_SESSION}, handle::flow::{basic::{FlowData, FlowRunLog, FlowStatus, FlowSubmitData}, run::FlowGraph}};





#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct FlowApp {
    pub(super) data: FlowData,
    pub(super) submit: FlowSubmitData,
    pub(super) machines: MachineConfigs,
}


impl TimerAppTrait for FlowApp {
    type Key = String;

    fn get_cmd_and_args(self) -> (String, Vec<String>) {
        let mut args: HashMap<String, HashSet<String>> = HashMap::new();
        for kv in self.submit.args.into_values() {
            for (k, v) in kv.into_iter() {
                match args.entry(k) {
                    Entry::Occupied(mut en) => en.get_mut().insert(v),
                    Entry::Vacant(en) => en.insert(HashSet::new()).insert(v),
                };
            }
        }
        (
            format!("Flow({})", self.data.setting.id),
            args.into_iter().map(| (k, v) | format!("{}={}", k, v.into_iter().collect::<Vec<_>>().join("@@@"))).collect::<Vec<_>>()
        )
    }

    fn get_note(&self) -> WResult<&str> {
        Ok(&self.data.setting.name)
    }

    async fn run(self) -> WResult<String> {
        let run_id = format!("{}-{}", Local::now().format("%Y%m%d%H%M%S"), random_token().split_off(19));
        let log_temp = log_format("Start ...".to_owned());
        let flow_status = FlowStatus::new(self.submit, log_temp, &run_id);

        let mut killer_rx = {
            let mut s = WEB_SESSION.flow.write().await;
            // flow start 
            let (tx, rx) = kill_channel(false);
            let run_id1 = run_id.clone();
            let run_id2 = run_id.clone();
            s.kill.insert(run_id1, tx);
            s.run.insert(run_id2, FlowRunLog {
                data: self.data.clone(),
                status: flow_status.clone()
            });
            rx
        };
        let flow_graph = FlowGraph::new(self.data, flow_status, self.machines)?;
        let run_id_res = run_id.clone();

        // run
        spawn(move || {
            let runtime = runtime_init(4).unwrap();
            runtime.block_on(async move {
                let mut log_data = Vec::with_capacity(2);
                let mut success = false;
                select! {
                    t = flow_graph.run() => {
                        match t {
                            Ok(false) => {
                                // running node error
                                log_data.push("Flow stop".to_owned());
                            },
                            Err(e) => {
                                // running node error
                                let log_temp = format!("Flow error: {}",  e);
                                log_data.push(log_temp);
                            },
                            Ok(true) => {
                                log_data.push("Flow done".to_owned());
                                success = true;
                            }
                        }
                    },
                    _ = killer_rx.changed() => {
                        log_data.push("Shutting down flow ...".to_owned());
                    }
                }
                let mut flow_storage = WEB_SESSION.flow.write().await;
                // killer
                let _ = flow_storage.kill.remove(&run_id);
                // done
                flow_storage.run.data
                    .get_mut(&run_id)
                    .unwrap()
                    .done(success, log_data);
            });
            runtime.shutdown_background();
        });
        Ok(run_id_res)
    }

    async fn get_all_timer() -> WResult<Vec<String>> {
        let r = WEB_SESSION.flow.read().await
            .timer.data
            .keys()
            .cloned()
            .collect::<Vec<_>>();
        Ok(r)
    }

    async fn load(f: &String) -> WResult<(Self, Vec<String>)> {
        WEB_SESSION.flow.read().await
            .timer.data
            .get(f)
            .cloned()
            .ok_or(WError::Empty)
    }
    
    async fn save(self, name: &str, log: &[String]) -> WResult<()> {
        WEB_SESSION.flow.write().await
            .timer.data
            .insert(name.to_string(), (self, log.to_vec()));
        Ok(())
    }

    fn timer_str(timer: &String) -> &str {
        timer.as_str()
    }
}

